package jdbc;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.h2.jdbcx.JdbcConnectionPool;

import xml.ConnectionPoolHolder;

import logic.Logic;

public abstract class DataAccessObject {
	private static final Logger log = Logger.getLogger(DataAccessObject.class);
	private static Properties dbProps;
	private static String URL, USER, PASSWORD, DRIVER, PATH, MAX_CONN;
	private ConnectionPoolHolder cph = ConnectionPoolHolder.getInstance();
	private JdbcConnectionPool cp;
	protected Connection conn;

	static {
		try {
			dbProps = new Properties();
			//Magic
			dbProps.load(DataAccessObject.class.getResourceAsStream("/db.properties"));

			URL = dbProps.getProperty("url");
			PATH = dbProps.getProperty("path");
			USER = dbProps.getProperty("user");
			PASSWORD = dbProps.getProperty("password");
			DRIVER = dbProps.getProperty("driver");
			MAX_CONN = dbProps.getProperty("maxConnections");
		} catch (IOException e) {
			log.error("Cant load DB properties!", e);
		}
	}

	private Method findGetTypeMethod(Class<?> parseToClass) throws SecurityException, NoSuchMethodException {
		String methodName = "get" + parseToClass.getSimpleName();
		Class<?>[] parseSourceClasses = { String.class };
		Method getTypeMethod = ResultSet.class.getDeclaredMethod(methodName, parseSourceClasses);
		getTypeMethod.setAccessible(true);
		return getTypeMethod;
	}

	protected void setFieldFromResultSet(ResultSet res, Object obj, String prefix, Field f)
			throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException {
		String fieldName = f.getName();
		String columnName = prefix + fieldName.toUpperCase();
		String fieldClassName = f.getType().getName();
		Object value = null;
		if (Logic.isSimpleClass(fieldClassName)) {
			Class<?> type = f.getType();
			Method getTypeMethod = findGetTypeMethod(type);
			value = getTypeMethod.invoke(res, columnName);
			if (!res.wasNull()) {
				f.set(obj, value);
			}
		}
	}

	public void start() throws SQLException {
		synchronized (cph) {
			if (cp == null) {
				cph = ConnectionPoolHolder.getInstance();
				try {
					//Magic
					Class.forName(DRIVER);
				} catch (ClassNotFoundException e) {
					log.error("Cant load the Driver for database!", e);
				}
				cp = JdbcConnectionPool.create(URL, USER, PASSWORD);
				cp.setMaxConnections(Integer.parseInt(MAX_CONN));
				cph.setConnectionPool(cp);
			}
		}
	}

	public void close() {
		try {
			if (conn != null) {
				conn.close();
			}
		} catch (SQLException e) {
			log.info("Exception occured while closing connection!", e);
		}
	}

	protected Connection getConnection() throws SQLException {
		conn = cp.getConnection();
		return conn;
	}

	public static void silentClose(Connection conn) {

		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				log.info("Cant close connection", e);
			}
		}

	}

}
